Análisis cartera de clientes
RFM Análisis¶
Este iPython Notebbok explica como realizar un análisis de clientes en base a los datos históricos, La idea es ver cual es el estado de salud de mi cartera de clientes.
#importamos las siguientes librerías
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from datetime import datetime
import squarify
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
#ruta del archivo con los datos
ruta='C:/Users/alfredo/Documents/Diplomado'
#nombre de las columnas
columns_name=['pk', 'sociedad', 'sector', 'ofvta', 'cod_cliente', 'nombre', 'vendedor', 'clas_fiscal',
'pedido', 'material', 'marca', 'linea', 'linea_o', 'id_pedido', 'costo', 'gr_mat', 'fecha',
'fecha_2', 'ingreso', 'mesn', 'dia', 'mes', 'año', 'funica' ]
df=pd.read_csv(ruta+'/Query1.txt', sep=';', decimal=',', encoding = 'ISO-8859-1', names=columns_name, header=0)
#imprimimos las 5 primeras filas del dataframe
df.head()
#verificamos los tipos de datos
df.info()
#modificamos el formato de las siguientes variables a "category" para ahorrar un poco más de memoria
df[['sociedad','sector','ofvta','clas_fiscal','marca','linea','linea_o']]=df[['sociedad','sector','ofvta','clas_fiscal','marca'
,'linea',
'linea_o']].apply(lambda x: x.astype('category'))
#transformo la fecha a formato date
df['Fecha_factura']=pd.to_datetime(df['fecha'], format='%d/%m/%Y')
#modifico el formato de los ingresos y costos
df[['ingreso', 'costo']] = df[['ingreso', 'costo']].apply(lambda x: x.astype(np.int64))
#elimino los registros con ingreso 0 en las filas (esto es un error del ERP)
df=df[df.ingreso != 0.0]
#imprimo estadísticos básicos de venta
df[['ingreso']].groupby(lambda x: df['Fecha_factura'][x].year).describe()
1 Creación tabla RFM¶
startDate_history = datetime.strptime('20150901', '%Y%m%d').date()
endDate_history=datetime.strptime('20160229', '%Y%m%d').date()
def getRFM(dataFrame, sDate, eDate):
"""
Función que calcula la frecuencia, ingreso, la recencia de compra y si utilizó servicio técnico
"""
#ordeno el dataframe por fecha descendiente
dataFrame=dataFrame.sort_values(by='Fecha_factura', ascending=False)
#filtro los datos segun la fecha histórica y los almaceno en un df temporal
tmpdf=dataFrame[(dataFrame.Fecha_factura >= sDate) & (dataFrame.Fecha_factura <= eDate)]
#elimino filas con los ID duplicados, asigno a una nuevo df modifico el indice para que sea los ID de cliente
newdf=tmpdf.drop_duplicates(['cod_cliente']).set_index(['cod_cliente'])
#calculo la recencia (días), menores días significa más reciente
newdf['recency']=(eDate-newdf['Fecha_factura']).dt.days
#calculo la frecuencia de compra
frecuency=tmpdf.groupby(['cod_cliente'] )['cod_cliente'].count()
#join la variable frecuencia con newdf
newdf=newdf.join(frecuency)
#renombro las columnas
newdf=newdf.rename(columns={'cod_cliente': 'frecuency'})
#calculo el ingreso
monetary=tmpdf.groupby(['cod_cliente'] )[['cod_cliente','ingreso']].sum()
newdf=newdf.merge(monetary, left_index=True,right_index=True)
newdf=newdf.rename(columns={'ingreso_y': 'monetary'})
#returno un nuevo df con la recencia, frecuencia, ingreso y st
return newdf
#tomamos los datos en un rango de fecha y lo almaceno en la variable historia
historia=getRFM(df, startDate_history, endDate_history)
#solo voy a trabajar con los datos recency, frecuency, monetary y los almaceno en la variable rfmTabla
rfmTabla=historia[['recency', 'frecuency', 'monetary']]
rfmTabla.head(5)
2 Determinando RFM¶
#ordeno de los montos de mayor a menor
test= pd.DataFrame( rfmTabla['monetary'].sort_values(ascending=False))
#Calculo el porcentaje de cada cliente
test['pc']= 100*test['monetary']/test['monetary'].sum()
#Almaceno el total acumulado en la variable cum_pc
test['cum_pc']=test['pc'].cumsum()
#clasifico por pareto el 50% como 1, entre 50% a 80% como 2 y sobre el 80% 3
test['t_monetary'] = test['cum_pc'].apply(lambda x: 1 if x > 80 else( 3 if x < 50 else 2))
test.head()
#corto la recency y la frecuencia según los percentil 33%, 66% y 99,9%
rfmTabla['t_recency']=pd.qcut(rfmTabla['recency'],3, labels=[3,2,1])
rfmTabla['t_frecuency']=pd.qcut(rfmTabla['frecuency'],3, labels=[1,2,3])
rfmTabla=rfmTabla.merge(test, left_index=True, right_index=True)
rfmTabla.head()
#concateno los resultados en la variable RFMClase
rfmTabla['RFMClase']=rfmTabla['t_recency'].astype('str') + rfmTabla['t_frecuency'].astype('str') + rfmTabla['t_monetary'].astype('str')
rfmTabla.head(5)
3 Categorizando RFM¶
#importo un archivo con la clasificación de todas las categorías
categoria=pd.read_csv(ruta+'/categoria.txt', sep='\t', encoding = 'ISO-8859-1', header=0)
categoria['RFMClase']=categoria['RFMClase'].astype('str')
categoria.info()
rfmTabla.reset_index(level=0, inplace=True)
rfmTabla.head()
#realizo un join entre las cateorias
test= rfmTabla.merge(categoria, left_on=['RFMClase'], right_on=['RFMClase'], how='outer')
test.head(5)
grupo=test.groupby(['clase']).size().reset_index(name='counts')
grupo
grupo['sales_ratio'] = 100*(grupo['counts']/grupo['counts'].sum())
#matplotlib para escalar los datos entre el mínimo, máximo, así le asignamos una escala a nuestros datos.
norm = matplotlib.colors.Normalize(vmin=min(grupo.counts), vmax=max(grupo.counts))
colors = [matplotlib.cm.Blues(norm(value)) for value in grupo.counts]
#crear un gráfico y redimensionarlo
fig = plt.gcf()
ax = fig.add_subplot()
fig.set_size_inches(16, 8)
labels = ["%s\n%d clientes\n%.2f %% " % (label) for label in zip(grupo.clase, grupo.counts, grupo.sales_ratio)]
#Usa la librería squarify para graficar nuestros datos, se etiqueta y agrega color
squarify.plot(label=labels ,sizes=grupo.counts, color = colors, alpha=.6)
plt.title("Estado cartera de clientes",fontsize=23,fontweight="bold")
plt.axis('off')
plt.show()
4 Conclusiones¶
Podemos revisar el estado en que se encuentra nuestra cartera de clientes, para el análisis se utilizaron las siguientes categorias
Campeón – 333: Compra recientemente, con frecuencia y nos deja mucho dinero representa el 50% de todos los ingresos de la compañia.
Cliente fiel – X3X: Compra con regularidad y tiene una aceptable frecuencia, representa el 30% de los ingresos de la compañia.
Cliente potencial fiel X2X: Clientes que tienen cierta regularidad y nos deja buenos ingresos.
Cliente valioso con riesgo 12X: Clientes que hace un tiempo no nos visitan y que regularmente nos dejaban buenos ingresos.
Cliente no se puede perder 113: Clientes que hace un tiempo no nos visitan y que regularmente nos dejaban muchos ingresos (pertenecen al segmento que representa el 50% de los ingresos de la compañia).
Cliente poco fidelizado XX1: Clientes que nos visitan esporádicamente y sus ingresos no son significativos.
Cliente perdido 111: Clientes hace un buen tiempo no nos visitan, su frecuencia es ocasional y sus ingresos mo son significativos.